#ifndef NET_TCPCONNECTION_H
#define NET_TCPCONNECTION_H

#include "Sockaddr.h"
#include "callback.h"
#include "utility/Noncopyable.h"

#include <optional>
#include <string_view>

class Eventloop;
class IOchannel;
class Socket;

class TCPConnection final : public std::enable_shared_from_this<TCPConnection> {
private:
        /// @code
    ///      |
    ///     TCPConnection()
    ///      V
    /// _CONNECTING
    ///      |
    ///     established()
    ///      V
    /// _CONNECTED -------------
    ///      |                 |
    ///     force_close()      |
    ///   / shutdown()         |
    ///      V                destroy()
    /// _DISCONNECTING      / _close_callback()
    ///      |                 |
    ///     _close_callback()  |
    ///      V                 |
    /// _DISCONNECTED <---------
    ///      |
    ///     ~TCPConnection()
    ///      V
    enum class _State {
         _DISCONNECTED,
        _CONNECTING,
        _CONNECTED,
         _DISCONNECTING
    };

public:
    TCPConnection(Eventloop &loop, const std::string &name,
                  std::unique_ptr<Socket> &&socket,  const Sockaddr &local,
                  const Sockaddr &peer);
    ~TCPConnection();

    Eventloop &loop() const { return _loop; }
    const std::string &name() const { return _name; }
    const Sockaddr &local() const { return _local; }
    const Sockaddr &peer() const { return _peer; }
    bool is_connected() const { return _state == _State::_CONNECTED; }

    void set_connection_callback(const ConnectionCallback &callback);
    void set_close_callback(const CloseCallback &callback);
    void set_message_callback(const MessageCallback &callback);

    /// @brief run in owner's thread
    void established();
    /// @brief run in owner's thread
    void destroy();

private:
    NONCOPYABLE(TCPConnection);

    void _read_callback(Utility::timepoint t);
    void _write_callback();
    void _error_callback();
    void _close_callback();
    void _set_state(_State s) { _state = s; }
    const char *_state_to_string() const;

    const std::string _name;
    CloseCallback _closeCallback;
    ConnectionCallback _connectionCallback;
    MessageCallback _messageCallback;
    const Sockaddr _local;
    const Sockaddr _peer;
    Eventloop &_loop;
    const std::unique_ptr<Socket> _socket;
    std::unique_ptr<IOchannel> _channel;
    _State _state;
};


#endif